package com.isyscore.cse.sdk

import com.isyscore.kotlin.common.runCommand
import java.io.File
import java.util.*

enum class ScriptMode {
    File, Binary
}

class CrossScriptExecutor {
    /* 脚本文件路径 */
    var codeFilePath = ""
    /* 二进制文件路径 */
    var binaryPath = ""
    /* 脚本执行模式 */
    var mode = ScriptMode.File
    /* 引用文件的路径 */
    var includePath = "."
    /* 异常信息 */
    var errorMessage = ""
    /* 执行返回信息 */
    var outputMessage = ""
    /* 执行超时时间 */
    var executeTimeout = 5000L

    companion object {

        fun executorText(code: String): String {
            val uuid = UUID.randomUUID().toString()
            val fCode = File(scriptPath, "$uuid.code").apply { writeText(code) }
            val se = CrossScriptExecutor().apply {
                codeFilePath = fCode.absolutePath
                mode = ScriptMode.File
            }
            scriptMap[uuid] = se
            return uuid
        }

        fun executorFile(filePath: String): String {
            val uuid = UUID.randomUUID().toString()
            val se = CrossScriptExecutor().apply {
                codeFilePath = filePath
                mode = ScriptMode.File
            }
            scriptMap[uuid] = se
            return uuid
        }

        fun executorBinary(filePath: String): String {
            val uuid = UUID.randomUUID().toString()
            val se = CrossScriptExecutor().apply {
                binaryPath = filePath
                mode = ScriptMode.Binary
            }
            scriptMap[uuid] = se
            return uuid
        }
    }

    fun run(): Boolean {
        val execFile = if (mode == ScriptMode.File) {
            val (b, _) = compile()
            if (b) binaryPath else ""
        } else binaryPath
        return if (execFile != "") {
            val runRet = runCommand {
                timeout = executeTimeout
                workDir = File("./").absolutePath
                commands.add(CrossScriptEngine.ISC)
                commands.add("-p")
                commands.add(CrossScriptEngine.nativePluginPath)
                commands.add("-i")
                commands.add(includePath)
                commands.add("-f")
                commands.add(execFile)
            }
            if (runRet.output.contains("access violation", ignoreCase = true)) {
                errorMessage = runRet.output
                false
            } else {
                outputMessage = runRet.output
                true
            }
        } else {
            false
        }
    }

    fun compile(): Pair<Boolean, String> =
        if (mode == ScriptMode.File) {
            // compile first
            val outPath = scriptPath
            val compileRet = runCommand {
                workDir = outPath.absolutePath
                commands.add(CrossScriptEngine.ISCC)
                commands.add("-p")
                commands.add(CrossScriptEngine.nativePluginPath)
                commands.add("-i")
                commands.add(includePath)
                commands.add("-f")
                commands.add(codeFilePath)
                commands.add("-o")
                commands.add(outPath.absolutePath)
            }
            if (compileRet.output.contains("COMPILE SUCC", ignoreCase = true)) {
                binaryPath = outPath.absolutePath + File.separator + codeFilePath.substringAfterLast(File.separator).substringBeforeLast(".")
                mode = ScriptMode.Binary
                true to binaryPath
            } else {
                errorMessage = compileRet.output
                false to ""
            }
        } else (true to binaryPath)

}

val scriptPath = File("./scripts").apply { if (!exists()) mkdirs() }
val scriptMap = mutableMapOf<String, CrossScriptExecutor>()